#include "PrecompiledHeader_Reborn.h"
#include "GalaxyField.h"

/*--------------------------------------------------------------------------*/

GalaxyField::GalaxyField(const NodeCreation& nc) :
	ICelestial(nc, ECelestialType::Galaxyfield)
{
	this->m_searchNearestStar = false;

	this->SetRadius(FLT_MAX);
	this->SetScale(1.0f);

	RenderTaskDesc desc;
	desc.m_material = NULL;
	desc.m_argument = 10;
	desc.m_flags = ERenderTaskFlag::NoMaterial;
	desc.m_priority = 0;
	desc.m_object = this;
	this->AddRenderTask(desc);

	NebulaCreation nec;
	nec.m_center = glm::vec3();
	nec.m_color = Color(250, 250, 250, 250);
	nec.m_deviation = glm::vec3(7000.0f, 7000.0f, 7000.0f);
	nec.m_quadradius = 700.0f;
	nec.m_numQuads = 4000;
	nec.m_type = ENebulaType::Spiral;

	Nebula* nebula = new Nebula(nc, nec);

	this->SetOcclusionTestEnabled(false);
	this->SetFrustumTestEnabled(false);
}

/*--------------------------------------------------------------------------*/

GalaxyField::~GalaxyField()
{

}

/*--------------------------------------------------------------------------*/

void GalaxyField::RenderableBinder(uint treeCalcFlags)
{
	this->BindRenderTask(this->GetSceneInstance()->GetRenderPhase(), 10);
}

/*--------------------------------------------------------------------------*/

void GalaxyField::RenderableHelper(const uint i, ERenderQuality::Type quality, uint pendingInstances)
{
	if (i != 10)
	{
		return;
	}

#ifdef VL_RENDER_STATISTICS
	g_Hud->GetStatisticsManager()->IncrementStatistic("GalaxyField");
#endif

	this->DrawStars();
}

/*--------------------------------------------------------------------------*/

void GalaxyField::GenerateContent()
{
	this->SetName("Universe");

	this->PrepareGalaxyField();
	this->PrepareStars();
}

/*--------------------------------------------------------------------------*/

void GalaxyField::PrepareStars()
{
	/*--------------------------------------------------------------------------*/

	std::string name = "GalaxyField_" + this->GetName() + "_galaxies_";

	this->m_galaxyFieldVao = ROpenGL::GenVertexArray(name + "VAO",
	                                                 GL_POINTS,
	                                                 this->m_numGalaxies);

	GLVertexBuffer* galaxiesVBpos = ROpenGL::GenBuffer(name + "pos", EVertexDeclaration::Position0, this->m_numGalaxies);
	GLVertexBuffer* galaxiesVBclr = ROpenGL::GenBuffer(name + "clr", EVertexDeclaration::Color0, this->m_numGalaxies);
	GLVertexBuffer* galaxiesVBnfo = ROpenGL::GenBuffer(name + "nfo", EVertexDeclaration::Color1, this->m_numGalaxies);

	/*--------------------------------------------------------------------------*/

	{
		glm::vec3* v = NULL;
		v = (glm::vec3*) ROpenGL::MapBuffer(galaxiesVBpos);

		for (uint i = 0; i < this->m_numGalaxies; ++i)
		{
			v[i] = this->m_starSystems[i].m_position;
		}

		ROpenGL::UnmapBuffer(galaxiesVBpos);
	}

	{
		Color* v = NULL;
		v = (Color*) ROpenGL::MapBuffer(galaxiesVBclr);

		for (uint i = 0; i < this->m_numGalaxies; ++i)
		{
			v[i] = this->m_starSystems[i].m_color;
		}

		ROpenGL::UnmapBuffer(galaxiesVBclr);
	}

	{
		glm::vec4* v = NULL;
		v = (glm::vec4*) ROpenGL::MapBuffer(galaxiesVBnfo);

		for (uint i = 0; i < this->m_numGalaxies; ++i)
		{
			v[i].x = this->m_starSystems[i].m_strength;
		}

		ROpenGL::UnmapBuffer(galaxiesVBnfo);
	}
	/*--------------------------------------------------------------------------*/

	ROpenGL::BindVertexArray(this->m_galaxyFieldVao);
	ROpenGL::AttachBuffer(galaxiesVBpos);
	ROpenGL::AttachBuffer(galaxiesVBclr);
	ROpenGL::AttachBuffer(galaxiesVBnfo);

	ROpenGL::UniqueBufferOwner(m_galaxyFieldVao, galaxiesVBpos);
	ROpenGL::UniqueBufferOwner(m_galaxyFieldVao, galaxiesVBclr);
	ROpenGL::UniqueBufferOwner(m_galaxyFieldVao, galaxiesVBnfo);

	this->m_galaxyFieldMaterial = g_Material->GetMaterial("mat_galaxy");
}

/*--------------------------------------------------------------------------*/

void GalaxyField::DestroyContent()
{
	ROpenGL::DeleteVertexArray(&this->m_galaxyFieldVao);
}

/*--------------------------------------------------------------------------*/

void GalaxyField::DrawStars()
{
	if (this->m_galaxyFieldMaterial && this->m_galaxyFieldVao)
	{
		g_Material->BindMaterial(this->m_galaxyFieldMaterial, EMaterialTechnique::Default);
		ROpenGL::BindVertexArray(this->m_galaxyFieldVao);

		glEnable(GL_PROGRAM_POINT_SIZE);
		glEnable(GL_POINT_SPRITE);
		glEnable(GL_POINT_SMOOTH);

		ROpenGL::DrawVertexArray(0);
	}
}

/*--------------------------------------------------------------------------*/

void GalaxyField::PrepareGalaxyField()
{
	this->m_numGalaxies		= 40000;
	this->m_testsPerFrame	= 100;
	this->m_testStartPoint	= 0;

	std::default_random_engine generator;
	std::normal_distribution<float> distribution(0.0f, 2.0f);
	StarSystemDesc ssd;

	NodeCreation nc;
	nc.m_flags = 0;
	nc.m_parent = this->GetParent();
	nc.m_scene = this->GetSceneInstance();

	NebulaCreation nec;
	nec.m_center = glm::vec3();
	nec.m_numQuads = 400;

	uint starsSpendSoFar = 0;
//
// 	for (uint i = 0; i < 25; ++i)
// 	{
// 		glm::vec3 position = glm::vec3(distribution(generator),
// 		                               distribution(generator),
// 		                               distribution(generator)) * 2000.0f;
//
// 		//for (uint j = 0; j < 10; ++j)
// 		{
// 			nec.m_quadradius = RandFloat(400.0f, 600.0f);
// 			nec.m_deviation = glm::vec3(nec.m_quadradius * 5, nec.m_quadradius * 5, nec.m_quadradius * 5);
// 			nec.m_type = ENebulaType::Spiral;
// 			nec.m_color = Color::Random();
//
// 			Nebula* nebula = new Nebula(nc, nec);
// 			nebula->SetPosition(position);//glm::vec3(RandFloat(-r, r), RandFloat(-r, r), RandFloat(-r, r)));
// 		}
// 	}

	for (uint j = 0; j < this->m_numGalaxies; ++j)
	{
		ssd.m_position	= glm::vec3(distribution(generator),
		                            distribution(generator),
		                            distribution(generator)) * 2000.0f;

		//ssd.m_position = glm::vec3(RandFloat(-r, r), RandFloat(-r, r), RandFloat(-r, r));
		ssd.m_strength	= RandFloat(1.0f, 2.5f);
		ssd.m_color		= Color::White;//(RandFloat(0.1f, 1.0f), RandFloat(0.1f, 1.0f), RandFloat(0.5f, 1.0f), 1.0f);
		ssd.m_id		= j;

		this->m_starSystems.push_back(ssd);
	}
}

/*--------------------------------------------------------------------------*/

const glm::vec3& GalaxyField::GetChildPosition(uint starid)
{
	return this->m_starSystems[starid].m_position;
}

/*--------------------------------------------------------------------------*/

void GalaxyField::OnEnter()
{
	this->m_searchNearestStar = true;
}

/*--------------------------------------------------------------------------*/

void GalaxyField::OnLeave()
{
	this->m_searchNearestStar = false;
}

/*--------------------------------------------------------------------------*/

bool GalaxyField::CanBeHost()
{
	return true;
}

/*--------------------------------------------------------------------------*/

CameraDesc GalaxyField::GetChildCameraDesc()
{
	CameraDesc cd;
	cd.m_farplane = 1000000000.0f;
	cd.m_nearplane = 1.0f;
	cd.m_fov = 70.0f;
	cd.m_method = ERenderPhaseMethod::DeferredShading;

	return cd;
}

/*--------------------------------------------------------------------------*/

void GalaxyField::OnPreCalc(uint arg)
{
	if (this->m_searchNearestStar) // znaczy ze jestem hostem
	{
		glm::vec3 campos = this->GetSceneInstance()->GetCamera()->GetPosition();

		for (uint i = this->m_testStartPoint; i < this->m_numGalaxies && i < this->m_testStartPoint + this->m_testsPerFrame; ++i)
		{
			if (glm::distance(campos, this->m_starSystems[i].m_position)  < 2.0f)
			{
				g_Explorer->Push(this, ECelestialType::Star, &this->m_starSystems[i], glm::normalize(campos - this->m_starSystems[i].m_position));
			}
		}

		this->m_testStartPoint += this->m_testsPerFrame;

		if (this->m_testStartPoint > this->m_numGalaxies)
		{
			this->m_testStartPoint = 0;
		}
	}
}

/*--------------------------------------------------------------------------*/

void GalaxyField::Log()
{
	std::cout << "GalaxyField: " << this->GetName() << "\n";
	std::cout << "   Galaxies: " << this->m_numGalaxies << "\n";
}

/*--------------------------------------------------------------------------*/